﻿#! /usr/bin/python
from asyncio import protocols
import tkinter as tk,  tkinter.ttk as ttk
import tkinter.simpledialog, tkinter.messagebox, tkinter.scrolledtext
import socket,select, time,datetime
import platform,subprocess,serial.tools.list_ports
from turtle import back
from threading import Thread, Semaphore
from collections import deque
import ui_data,channel, raw, mainlist,mainframe,mainstate
import about
# try:
#     import serial
# except ImportError:
#     try:
#         import pip
#     except ImportError:
#         pass

class UI (ui_data.UI_data):
    def __init__(self, master=None, geometry = [300,200,-20,-20], center = False) -> None:
        super(ui_data.UI_data).__init__()
        self.dqrecv:deque = deque()
        self.dqsend:deque = deque()
        self.ADATA:dict = {} # 所有数据保存在此, 每个数据的结构为 AData
        self.pthread_net = channel.create_thread_net(self.dqrecv, self.dqsend)
        self.update_UI_ID = None
        #创建一个线程 for 串口，因为 windows下不能用 select for 串口
        pthread_com = None

        if master== None:
            self.root = tk.Tk()
        else:
            self.root =  master
        g = geometry
        if center:
            g[2] = (tk.Misc.winfo_screenwidth(self.root) - geometry[0])/2
            g[3] = (tk.Misc.winfo_screenheight(self.root) - geometry[1])/2
        # tk.Wm.geometry(self.root , "{0:.0f}x{1:.0f}{2:+.0f}{3:+.0f}".format(*g))
        self.root.wm_geometry("{0:.0f}x{1:.0f}{2:+.0f}{3:+.0f}".format(*g))
        self.root.iconphoto(False,  tk.PhotoImage(data=about.ICO))
        
        self.root.protocol("WM_DELETE_WINDOW", self.quit)
        
        self.root.minsize(300, 200)
        self.root.wm_title("通讯调试狗")
        self.root.option_add('*Menu.tearOff', False)
        w = tk.PanedWindow(self.root, background="#33CC33")#, showhandle = True) #主窗口
        w.pack(fill="both", expand = 1,  side="top")
        self.sml = mainlist.MainList(w, self)
        w.add(self.sml)
        w.paneconfigure(self.sml, minsize = 100, width = 200, sticky = "nswe")
        self.smf = mainframe.MainFrame(w, self)
        w.add(self.smf)
        w.paneconfigure(self.smf,minsize=200, sticky="nswe")
        self.sms = mainstate.MainState(self.root, self)  # 状态栏
        # tk.Button(self.state, text="aaaa", command= lambda:print(self.state.winfo_geometry())).grid()
        self.sms.pack(after=w, fill="x", anchor="s",side="bottom")
        # self.pWindow = [ml, mf, ms, self.allconf , pthread_net, pthread_com]
        #
        #定期更新UI
        self.update_UI()
        pass

    def update_UI(self):
        """定期更新UI"""
        if (self.dqrecv):
            msg = self.dqrecv.pop()
            if(msg):
                if(msg[0] == "accept"):
                    if(msg[1]== "TCPS"):
                        #收到一个 TCPS 的 TCPA 连接
                        sock_tcps:socket.socket = msg[2]
                        id_tcps:int = msg[3]
                        sock_tcpa:socket.socket = msg[4]
                        lip,lport = sock_tcpa.getsockname()
                        rip,rport = msg[5]
                        # rip,rport = sock_tcpa.getpeername()
                        if(id_tcps in self.ADATA):
                            data_tcps:ui_data.AData = self.ADATA[id_tcps]
                            data_tcpa = ui_data.AData(channel = channel.TCPA(fd = sock_tcpa,
                                                                            id = ui_data.get_UID(),
                                                                            lip=lip,lport=lport, 
                                                                            rip=rip,rport=rport,
                                                                            ups=data_tcps),
                                                     protocol = raw.RAW())
                            data_tcpa.protocol.set(ml=self.sml.add(data_tcpa), mf=self.smf.add(data_tcpa), ms=self.sms.add(data_tcpa))
                            self.ADATA[data_tcpa.channel.id] = data_tcpa
                            self.log("成功", "通道TCPS "+ str(data_tcps.channel.id) + " 接收连接：" + 
                                            data_tcpa.channel.info + " / " + 
                                            data_tcpa.protocol.info )
                            self.dqsend.append(("add", "TCP", data_tcpa.channel.fd, data_tcpa.channel.id,))
                        pass
                    elif (msg[1]== "err1"):
                        pass
                    elif(msg[1] == "err2"):
                        pass
                    pass
                elif(msg[0] == "recv"): # ("TCP","recv", sock:socket.socket, id,  data:bytes)
                    if(msg[1] == "TCP"): #收到报文
                        sock_tcp:socket.socket = msg[2]
                        id_tcp = msg[3]
                        recv_data = msg[4]
                        if(id_tcp in self.ADATA):
                            data:ui_data.AData = self.ADATA[id_tcp]
                            data.protocol.mf.update_recv_UI(recv_data)
                elif msg[0] == "recvfrom": # UDP 收到报文
                    if msg[1] == "UDP":
                        sock_udp:socket.socket = msg[2]
                        id_udp:int = msg[3]
                        recvfrom_data:bytes = msg[4]
                        addr_udp:tuple = msg[5]
                        if(id_udp in self.ADATA):
                            data:ui_data.AData = self.ADATA[id_udp]
                            data.protocol.mf.update_recv_UI(recvfrom_data, addr_udp)
                elif msg[0] == "read": # COM 收到报文
                    if msg[1] == "COM":
                        fd_com = msg[2]
                        id_com = msg[3]
                        read_data = msg[4]
                        if(id_com in self.ADATA):
                            data:ui_data.AData = self.ADATA[id_com]
                            data.protocol.mf.update_recv_UI(read_data)
                elif(msg[0] == "peerclose"): # TCP 对侧关闭连接
                    sock_tcp:socket.socket = msg[2]
                    id_tcp = msg[3]
                    if(id_tcp in self.ADATA):
                        data:ui_data.AData = self.ADATA[id_tcp]
                        data.channel.stoped2()
                        self.sms.update(data)
                        self.disable(data)
                        self.log("提示", "对侧关闭：" + data.channel.info + " / " + data.protocol.info )
                elif(msg[0] == "del"): # 套接字关闭 ， 更新状态
                    if(msg[3] in self.ADATA):
                        data = self.ADATA[msg[3]]
                        data.channel.stoped()
                        self.sms.update(data)
                        # self.log("成功", "命令关闭："+ str(data.channel.id) + ":" + data.channel.info + " / " + data.protocol.info)
                elif (msg[0] == "connect"): # 套接字连接客服端成功
                    if(msg[3] in self.ADATA):
                        data = self.ADATA[msg[3]]
                        data.channel.started()
                        self.enable(data)
                        self.update(data)
                        self.show(data)
                        self.log("成功", "启动：" + data.channel.info + " / " + data.protocol.info )
                elif(msg[0] == "err_select_x"): # 套接字 select 出错
                    if(msg[3] in self.ADATA):
                        data = self.ADATA[msg[3]]
                        if(data.channel.type == "TCPC"):
                            if(msg[4][1]): # 写出错，由此判定是 TCPC连接出错
                                data.channel.stoped3()
                                self.sms.update(data)
                                self.log("错误", "连接出错："+ str(data.channel.id) + ":" + data.channel.info + " / " + data.protocol.info)
                elif msg[0] == "exit":
                    if msg[1] == "COM":
                        fd_com:socket.socket = msg[2]
                        id_com = msg[3]
                        if(id_com in self.ADATA):
                            data:ui_data.AData = self.ADATA[id_com]
                            data.channel.stoped()
                            self.sms.update(data)
                            self.disable(data)
                            self.log("提示", "关闭：" + data.channel.info + " / " + data.protocol.info + " / " + str(msg[4]) )
                elif msg[0] == "err_com":
                    if msg[1] == "COM":
                        fd_com:socket.socket = msg[2]
                        id_com = msg[3]
                        if(id_com in self.ADATA):
                            data:ui_data.AData = self.ADATA[id_com]
                            data.channel.stoped()
                            self.sms.update(data)
                            self.disable(data)
                            self.log("提示", "串口出错关闭：" + data.channel.info + " / " + data.protocol.info + " / " + str(msg[4]) )
                else:
                    # print(msg)
                    pass
            else:
               time.sleep(0.01) 
        else:
            time.sleep(0.01)
            
        self.update_UI_ID = self.root.after(10, self.update_UI)
        pass
 
    # 调用 tk.Tk().mainloop
    def mainloop(self): # 
        self.root.mainloop()
        pass
    
    def quit(self):
        # 停止收发
        for key in self.ADATA:
            self.smf.remove(self.ADATA[key]) 
        self.dqsend.append(("quit",)) #通知线程退出
        self.pthread_net.join() # 一直等待这个线程关闭
        if self.update_UI_ID:
            self.root.after_cancel(self.update_UI_ID)
            self.update_UI_ID = None
        # 开始关闭 套接字
        for key in self.ADATA:
            data:ui_data.AData = self.ADATA[key]
            if(data.channel.fd):
                data.channel.fd.close()
        self.root.destroy()
        pass
    
    

    
# 创建一个实例
UI(tk.Tk(),[800,600,0,0], True).mainloop()
